#include "Logic.h"
#include "HttpConnection.h"
#include "../log.h"
#include "../NetGlobal.h"
#include "NetStruct.h"
#include "../ReflectJson.hpp"
#include "../GrpcClient/VerifyGrpcClient.h"
#include "../Singleton.h"
#include "../DB/Redis/RedisMgr.h"
#include "../DB/Redis/RedisConPool.h"
#include "../DB/MySql/MySqlConPool.h"
#include "../DB/MySql/UserDao.h"
#include "../Hash.h"
#include "../GrpcClient/StatusGrpcClient.h"

#include <sstream>


template<class T>
void sendResponse(std::shared_ptr<HttpConnection> conn, T& stu, const int code, const std::string& msg)
{
    stu.code = code;
    stu.msg = msg;
    std::string body = ToString(serialize(stu));
    boost::beast::ostream(conn->GetResponse().body()) << body;
}


Logic::Logic()
{
    RegisterGet("/test_get", [](std::shared_ptr<HttpConnection> conn) {
        std::stringstream oss;
        auto& parms = conn->GetParams();
        for(const auto& [key, value] : parms)
        {
            oss << key << ": " << value << "\t";
        }

        boost::beast::ostream(conn->GetResponse().body())
            << "Hello, this is a test response!\nparms: "
            << oss.str();
    });

    RegisterPost("/test_post", [](std::shared_ptr<HttpConnection> conn) {
        /*
        {
            "id": 10,
            "name": "lalala",
            "tags": [
                "aaa",
                "bbb"
            ],
            "scores": {
                "China": 1,
                "English": 2
            }
        }
        */

        TestAck ack;
        ack.code = ErrorCode::Success;
        ack.msg = "success";
        conn->GetResponse().set(boost::beast::http::field::content_type, "text/json");
        
        auto body = boost::beast::buffers_to_string(conn->GetRequest().body().data());
        log_info("Received POST request with body: {}", body);

        auto stu = deserialize<TestReq>(FromString(body));
        if (std::holds_alternative<std::string_view>(stu)) 
        {
            log_error("Failed to deserialize TestReq: {}", std::get<std::string_view>(stu));
            sendResponse(conn, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(stu)));
            return;
        }
        TestReq obj = std::get<TestReq>(stu);
        log_info("Received TestReq: id={}, name={}, tags={}, scores={}",
            obj.id, obj.name, obj.tags.size(), obj.scores.size());
        for(auto& tag : obj.tags)
        {
            log_info("tag: {}", tag);
        }
        for(auto& [subject, score] : obj.scores)
        {
            log_info("score: {} = {}", subject, score);
        }

        sendResponse(conn, ack, ErrorCode::Success, "success");
    });

    _SetCallBack();
}

void Logic::_SetCallBack()
{
    // 获取验证码
    RegisterPost("/get_verification_code", getVerificationCode);
    // 用户注册
    RegisterPost("/user_register", userRegister);
    // 修改密码
    RegisterPost("/modify_password", modifyPassword);
    // 用户登录
    RegisterPost("/user_login", userLogin);
}


bool Logic::HandleGet(std::string url, std::shared_ptr<HttpConnection> conn)
{
    auto find = _get_handlers.find(url);
    if(find == _get_handlers.end())
    {
        return false;
    }

    find->second(conn);
    return true;
}

void Logic::RegisterGet(const std::string& url, HttpHandler cb)
{
    _get_handlers.insert(std::make_pair(url, cb));
}


bool Logic::HandlePost(std::string url, std::shared_ptr<HttpConnection> conn)
{
    auto find = _post_handlers.find(url);
    if(find == _post_handlers.end())
    {
        return false;
    }

    find->second(conn);
    return true;
}

void Logic::RegisterPost(const std::string& url, HttpHandler cb)
{
    _post_handlers.insert(std::make_pair(url, cb));
}



void getVerificationCode(std::shared_ptr<HttpConnection> conn)
{
    auto body = boost::beast::buffers_to_string(conn->GetRequest().body().data());
    log_info("Received POST request with body: {}", body);

    GetVerificationCodeAck ack;
    conn->GetResponse().set(boost::beast::http::field::content_type, "text/json");

    auto req = deserialize<GetVerificationCodeReq>(FromString(body));
    if (std::holds_alternative<std::string_view>(req)) 
    {
        log_error("Failed to deserialize GetVerificationCodeReq: {}", std::get<std::string_view>(req));
        sendResponse(conn, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(req)));
        return;
    }
        
    GetVerificationCodeReq obj = std::get<GetVerificationCodeReq>(req);
    log_info("Received GetVerificationCodeReq: email={}", obj.email);

    //发送 rpc 请求到 node server
    auto grpcClient = Singleton<VerifyGrpcClient>::GetInstance()->GetVarifyCode(obj.email);

    ack.email = obj.email;
    sendResponse(conn, ack, grpcClient.code(), grpcClient.msg());
}

void userRegister(std::shared_ptr<HttpConnection> conn)
{
    auto body = boost::beast::buffers_to_string(conn->GetRequest().body().data());
    log_info("Received POST request with body: {}", body);

    UserRegisterAck ack;
    conn->GetResponse().set(boost::beast::http::field::content_type, "text/json");

    auto req = deserialize<UserRegisterReq>(FromString(body));
    if (std::holds_alternative<std::string_view>(req)) 
    {
        log_error("Failed to deserialize GetVerificationCodeReq: {}", std::get<std::string_view>(req));
        sendResponse(conn, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(req)));
        return;
    }

    UserRegisterReq obj = std::get<UserRegisterReq>(req);

    //判断验证码是否过期
    std::optional<std::string> value = std::nullopt;
    {
        auto redisConn = Singleton<RedisConPool>::GetInstance()->GetConnection();
        value = RedisMgr::Get(redisConn, VERIFICATION_CODE_PREFIX + obj.email);
    }
    if (!value)
    {
        log_error("Verification code timeout");
        sendResponse(conn, ack, ErrorCode::VarifyExpired, "Verification code timeout");
        return;
    }
    if(*value != obj.verificationCode)
    {
        log_error("Verification code error, value:{}, verifyCode:{}", *value, obj.verificationCode);
        sendResponse(conn, ack, ErrorCode::VarifyCodeErr, "Verification code error");
    }

    // 判断用户是否已经注册
    UserDao userDao(Singleton<MySqlPool>::GetInstance());
    auto user = userDao.GetUserByName(obj.username);
    if(!user.empty())
    {
        log_error("User already exists");
        sendResponse(conn, ack, ErrorCode::UserAlreadyExists, "User already exists");
        return;
    }
    user = userDao.GetUserByEmail(obj.email);
    if(user["email"] == obj.email)
    {
        log_error("Email already exists");
        sendResponse(conn, ack, ErrorCode::EmailAlreadyExists, "Email already exists");
        return;
    }

    Hash hash(Hash::SHA256_TYPE);
    auto pwd = hash.GetHash(Hash::SHA256_TYPE, (const unsigned char*)obj.password.c_str(), obj.password.size());
    if(pwd.empty())
    {
        log_error("Failed to hash password");
        sendResponse(conn, ack, ErrorCode::HashPasswordFailed, "Failed to hash password");
        return;
    }

    // 注册用户
    auto result = userDao.InsertUser(obj.username, obj.email, pwd);
    if(!result)
    {
        log_error("Failed to insert user");
        sendResponse(conn, ack, ErrorCode::InsertUserFailed, "Failed to insert user");
        return;
    }

    sendResponse(conn, ack, ErrorCode::Success, "success");
}   

void modifyPassword(std::shared_ptr<HttpConnection> conn)
{
    auto body = boost::beast::buffers_to_string(conn->GetRequest().body().data());
    log_info("Received POST request with body: {}", body);

    //数据和用户注册相同 都为 UserRegister
    UserRegisterAck ack;
    conn->GetResponse().set(boost::beast::http::field::content_type, "text/json");

    auto req = deserialize<UserRegisterReq>(FromString(body));
    if (std::holds_alternative<std::string_view>(req)) 
    {
        log_error("Failed to deserialize GetVerificationCodeReq: {}", std::get<std::string_view>(req));
        sendResponse(conn, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(req)));
        return;
    }

    UserRegisterReq obj = std::get<UserRegisterReq>(req);

    //判断验证码是否过期
    std::optional<std::string> value = std::nullopt;
    {
        auto redisConn = Singleton<RedisConPool>::GetInstance()->GetConnection();
        value = RedisMgr::Get(redisConn, VERIFICATION_CODE_PREFIX + obj.email);
    }
    if (!value)
    {
        log_error("Verification code timeout");
        sendResponse(conn, ack, ErrorCode::VarifyExpired, "Verification code timeout");
        return;
    }
    if(*value != obj.verificationCode)
    {
        log_error("Verification code error, value:{}, verifyCode:{}", *value, obj.verificationCode);
        sendResponse(conn, ack, ErrorCode::VarifyCodeErr, "Verification code error");
        return;
    }

    // 判断用户是否未注册
    UserDao userDao(Singleton<MySqlPool>::GetInstance());
    auto user = userDao.GetUserByName(obj.username);
    if(user.empty())
    {
        log_error("User not exists");
        sendResponse(conn, ack, ErrorCode::PasswdErr, "User not exists");
        return;
    }
    if(user["name"] != obj.username)
    {
        log_error("User name not match");
        sendResponse(conn, ack, ErrorCode::UserNameNotMatch, "User name not match");
        return;
    }
    if(user["email"] != obj.email)
    {
        log_error("Email not match");
        sendResponse(conn, ack, ErrorCode::EmailNotMatch, "Email not match");
        return;
    }

    Hash hash(Hash::SHA256_TYPE);
    auto pwd = hash.GetHash(Hash::SHA256_TYPE, (const unsigned char*)obj.password.c_str(), obj.password.size());
    if(pwd.empty())
    {
        log_error("Failed to hash password");
        sendResponse(conn, ack, ErrorCode::HashPasswordFailed, "Failed to hash password");
        return;
    }

    auto result = userDao.UpdatePasswordByName(obj.username, pwd);
    if(!result)
    {
        log_error("Failed to update password");
        sendResponse(conn, ack, ErrorCode::PasswdUpFailed, "Failed to update password");
        return;
    }

    sendResponse(conn, ack, ErrorCode::Success, "success");
}

void userLogin(std::shared_ptr<HttpConnection> conn)
{
    auto body = boost::beast::buffers_to_string(conn->GetRequest().body().data());
    log_info("Received POST request with body: {}", body);

    UserLoginAck ack;
    conn->GetResponse().set(boost::beast::http::field::content_type, "text/json");

    auto req = deserialize<UserLoginReq>(FromString(body));
    if (std::holds_alternative<std::string_view>(req)) 
    {
        log_error("Failed to deserialize UserLoginReq: {}", std::get<std::string_view>(req));
        sendResponse(conn, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(req)));
        return;
    }

    UserLoginReq obj = std::get<UserLoginReq>(req);

    // 判断用户密码是否正确
    UserDao userDao(Singleton<MySqlPool>::GetInstance());
    auto user = userDao.GetUserByEmail(obj.email);
    if(user.empty())
    {
        log_error("Email not exists");
        sendResponse(conn, ack, ErrorCode::EmailNotMatch, "Email not exists");
        return;
    }
    Hash hash(Hash::SHA256_TYPE);
    auto pwd = hash.GetHash(Hash::SHA256_TYPE, (const unsigned char*)obj.password.c_str(), obj.password.size());
    if(pwd != user["pwd"])
    {
        log_error("Password error, pwd:{}, userPwd:{}, password:{}", pwd, user["pwd"], obj.password);
        sendResponse(conn, ack, ErrorCode::PasswdErr, "Password error");
        return;
    }

    auto rsp = Singleton<StatusGrpcClient>::GetInstance()->GetChatServerInfo(std::stoi(user["id"]));
    if(rsp.code() != ErrorCode::Success)
    {
        log_error("GetChatServerInfo failed, code: {}, msg: {}", rsp.code(), rsp.msg());
        sendResponse(conn, ack, rsp.code(), rsp.msg());
        return;
    }
    
    ack.id = std::stoi(user["id"]);
    ack.port = rsp.port();
    ack.host = rsp.host();
    ack.token = rsp.token();
    sendResponse(conn, ack, ErrorCode::Success, "success");
}